package com.ncwu.infrastructure.util;

import io.swagger.annotations.ApiModelProperty;
import lombok.experimental.UtilityClass;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yanxinqing
 */
@UtilityClass
public class ObjectUtils {
    public void copyProperties(Object src, Object target){
        BeanUtils.copyProperties(src, target);
    }

    public void copyProperties(Object src, Object target, String... ignoreProperties){
        BeanUtils.copyProperties(src, target, ignoreProperties);
    }

    private String[] getNullPropertyNames(Object obj){
        final BeanWrapperImpl wrapper = new BeanWrapperImpl(obj);

        return Arrays.stream(wrapper.getPropertyDescriptors())
                .map(PropertyDescriptor::getName)
                .filter(propertyName -> wrapper.getPropertyValue(propertyName) == null)
                .toArray(String[]::new);
    }

    public String[] getProperties(Object obj){
        final BeanWrapperImpl wrapper = new BeanWrapperImpl(obj);

        return Arrays.stream(wrapper.getPropertyDescriptors())
                .map(PropertyDescriptor::getName)
                .filter(prop -> wrapper.getPropertyValue(prop) != null)
                .toArray(String[]::new);
    }

    public void copyNotNullProperties(Object src, Object target){
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }

    public void copyPropertiesWithoutId(Object src, Object target){
        BeanUtils.copyProperties(src, target, "id");
    }

    public <Src, Target> Target convert(Src src, Class<Target> clazz){
        if (src == null || clazz == null){
            return null;
        }

        try{
            Target result = clazz.getDeclaredConstructor().newInstance();
            copyProperties(src, result);

            return result;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    public <Src, Target> Target convert(Src src, Class<Target> clazz, String... ignoreProperties){
        if (src == null || clazz == null){
            return null;
        }

        try{
            Target result = clazz.getDeclaredConstructor().newInstance();
            copyProperties(src, result, ignoreProperties);

            return result;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    public <Src, Target> List<Target> convert(List<Src> src, Class<Target> clazz){
        if (src == null || clazz == null){
            return null;
        }

        return src.stream()
                .map(c->convert(c, clazz))
                .collect(Collectors.toList());
    }

    public <Src, Target> List<Target> convert(List<Src> src, Class<Target> clazz, String... ignoreProperties){
        if (src == null || clazz == null){
            return null;
        }

        return src.stream()
                .map(c->convert(c, clazz, ignoreProperties))
                .collect(Collectors.toList());
    }

}
